package graybox;
/**
*@author tonychao  
*一个调优参数空间
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/**
*@author tonychao 
*创建时间 ：2017年5月12日 下午1:44:43
*功能 一个参数区域，这个参数区域是可变的，并可以从文件配置中读入
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.TreeMap;

import javax.sql.rowset.FilteredRowSet;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import fetcher.SparkConfiguration;
import huristic.HuristicConf;
import others.Global;
import others.Global.SparkMode;
import shellInterface.UserSubmitInterface_test;
import others.DataType;


public class ParameterDistrict implements Cloneable {
	TreeMap<String, Scale> scaleMap=null;
	int originalLebegueSize;//理论上应该具有的龙贝格测度大小，实际值应该在这个值附近，由realLebegSize() 计算
	double totalShrinkFactor;//从最开始的全部空间到现在缩水了多少
	
	//一个浅克隆，因为区域必须是可以复制的
    @Override  
    public Object clone() {  
    	ParameterDistrict ret = null;  
        try{  
            ret = (ParameterDistrict) super.clone();  
        }catch(CloneNotSupportedException e) {  
            e.printStackTrace();  
        }  
        return ret;  
    }  
    
    /**
     * 从一个给定路径confFilePath+fileName的伪json文件中读取参数搜索空间的定义
     * @param confFilePath
     * @throws IOException 
     * @throws JSONException 
     */
    public void readFormFile(String confFilePath,String fileName) throws IOException, JSONException{
    	File file= new File(confFilePath+fileName);
    	if (!file.exists()){ //使用备选的文件目录
    		file= new File(fileName);
    		if (!file.exists()) {
    			System.err.println("没找到配置文件");
    			return;
    		}
    	}
    	
    	//从文件读取数据
    	BufferedReader reader=new BufferedReader(new FileReader(file));
    	String line="";
    	String str="";
    	while ((line=reader.readLine())!=null){
    		if (line.trim().startsWith("//")) continue; //注释
    		str+=line;
    	}
    	JSONArray dataJson= new JSONArray(str);
    	for(int i =0;i<dataJson.length();i++){
    		JSONObject paraBound= dataJson.getJSONObject(i);
    		String key= paraBound.getString("name");
    		int upperBound=paraBound.getInt("upperBound");
    	    int lowerBound=paraBound.getInt("lowerBound");
    	    int changeFactor=paraBound.getInt("changeFactor");
    	    Scale tmpScale= new Scale(upperBound, lowerBound, changeFactor);
    	    this.scaleMap.put(key, tmpScale);
    	}
    }
    
  
    public void readFromFile() throws IOException, JSONException{
    	readFormFile(Global.CONF_PATH,Global.PARAMETERT_DISTRICT_CONF_FILE);
    }
 
	    
	    
	/**
	 * 值域为[lowerBound,upperBound]
	 * 这个函数也是用来定义范围的 
	 * 每个参数都有上界和下界
	 * 这个函数中的默认值会被配置文件覆盖以适应不同集群的硬件资源环境
	 * 默认参数适应于科技园部署的大集群
	 * */
	public ParameterDistrict(ArrayList<String> groupNumberList) throws IOException, JSONException {
		this.scaleMap=new TreeMap<String,Scale>();	 
		this.totalShrinkFactor=1;
		//从文件中重载配置信息
		this.readFromFile();
		this.originalLebegueSize= getLebegueSize(groupNumberList);

	}
	
	/*返回一个参数空间大小的勒贝格测度
	 * 
	 * 
	 * */
	public int getLebegueSize(ArrayList<String> groupNumberList){
		int ret =1;
		String[] chosenParameterList = getChosenParameterList(groupNumberList);
		for (int i=0;i<chosenParameterList.length;i++){
			String tmp= chosenParameterList[i];
			if (tmp.equals("dummy")) return ret;
			ret*=scaleMap.get(tmp).getSize();
		}
		return ret;
	}
	
	
	
	/**
	 * 搜索空间进行缩水
	 * 返回false 说明空间太小
	 */
	public boolean shrink(SparkConfiguration center ,double factor,ArrayList<String> groupNumberList) {
		int LebegueSize =getLebegueSize(groupNumberList);

		
		
		if (LebegueSize<100){
			//System.err.println("不用收缩了，空间已经太小了"+getLebegueSize());
			return false;
		}
		
		//ArrayList<Double> point = center.writeToIntList();//点的位置
		if (Global.DEBUG_FLAG) System.out.println("目前采用的是均匀缩水法，对所有搜索空间的维度都进行缩水,应该分别对每个参数计算缩水比例");
		totalShrinkFactor*=factor;
		//对所有取值范围大于一的参数进行缩减
		double realShinkFactor=(totalShrinkFactor/(LebegueSize/(double)this.originalLebegueSize));//本轮参数空间进行缩减的比例
		//计算还有空间的参数个数
		
		
		String[] chosenParameterList = getChosenParameterList(groupNumberList);
		

		
		int Dcount =0;
		for (int i =0;i<chosenParameterList.length;i++){
			if (this.scaleMap.get(chosenParameterList[i]).getSize()>1){
				Dcount++;
			}
		}
		
		double  factorPerDemetion=Math.pow(realShinkFactor, 1.0/Dcount);
		
		if (Global.DEBUG_FLAG) System.out.println("需要收缩：\t"+realShinkFactor);
		if (Global.DEBUG_FLAG) System.out.println("可缩水的空间维度：\t"+Dcount);
		if (Global.DEBUG_FLAG) System.out.println("每一 维度收缩：\t"+factorPerDemetion);
		
		//开始对每一个维度进行缩水和舍入
		int corrent=-1,upper=-1,lower=-1;//现在的值
		
		for (int i=0; i<chosenParameterList.length;i++){
			if (scaleMap.get(chosenParameterList[i]).getSize()>1){//可缩水
				Scale tmpScale=scaleMap.get(chosenParameterList[i]);
				int correntValue=center.parameterMap.get(chosenParameterList[i]).value; //当前这一维度的中心值
				int newUpperBound=(int) (corrent+
						Math.round(    (( tmpScale.upperBound -corrent)/tmpScale.changeFactor)    *factorPerDemetion)*
						tmpScale.changeFactor);
				int newLowerBound=(int) (corrent-
						Math.round(((corrent- tmpScale.lowerBound)/tmpScale.changeFactor)*factorPerDemetion)*
						tmpScale.changeFactor);
				tmpScale.upperBound=newUpperBound;
				tmpScale.lowerBound=newLowerBound;
			}
		}
		
		if (Global.DEBUG_FLAG) System.out.println("shrink后的空间隆贝格测度：\t"+getLebegueSize(groupNumberList));
		return true;
	}
	
	
	public static String[] getChosenParameterList(ArrayList<String> groupNumberList){
		String[] chosenParameterList =new String[]{};
		
		for (int i =0;i<groupNumberList.size();i++){
			switch (groupNumberList.get(i)) {
			case "group0":
				chosenParameterList=Arrays.copyOf(chosenParameterList,chosenParameterList.length+Global.CHOSEN_PARAMETERS_GROUP0.length);
				System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP0,0,chosenParameterList,chosenParameterList.length-Global.CHOSEN_PARAMETERS_GROUP0.length,Global.CHOSEN_PARAMETERS_GROUP0.length);
				
				break;
			case "group1":
				chosenParameterList=Arrays.copyOf(chosenParameterList,chosenParameterList.length+Global.CHOSEN_PARAMETERS_GROUP1.length);
				System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP1,0,chosenParameterList,chosenParameterList.length-Global.CHOSEN_PARAMETERS_GROUP1.length,Global.CHOSEN_PARAMETERS_GROUP1.length);

				break;
			case "group2":
				chosenParameterList=Arrays.copyOf(chosenParameterList,chosenParameterList.length+Global.CHOSEN_PARAMETERS_GROUP2.length);
				System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP2,0,chosenParameterList,chosenParameterList.length-Global.CHOSEN_PARAMETERS_GROUP2.length,Global.CHOSEN_PARAMETERS_GROUP2.length);

				break;
			case "group3":
				chosenParameterList=Arrays.copyOf(chosenParameterList,chosenParameterList.length+Global.CHOSEN_PARAMETERS_GROUP3.length);
				System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP3,0,chosenParameterList,chosenParameterList.length-Global.CHOSEN_PARAMETERS_GROUP3.length,Global.CHOSEN_PARAMETERS_GROUP3.length);

				break;
			default:
				UserSubmitInterface_test.UIErr("参数分区错误！");
				break;
			}
		}
		return chosenParameterList;
	}
	
	
	public SparkConfiguration randomSparkConfiguration(ArrayList<String> groupNumberList){
		SparkConfiguration ret= new SparkConfiguration();
		String[] randomParameterList=getChosenParameterList(groupNumberList);		    
		for (String key : randomParameterList){
			if (key.equals("dummy")) continue;
			ret.parameterMap.get(key).value=this.scaleMap.get(key).Random();
		}
		return ret;
	}

	
	
/*	
	//从所给出的区域中给出一个完全随机的参数点，该参数点包括所有的维度,但是他的随机维度只有1，2，和3，不包括第0组 XXXXX
	public SparkConfiguration randomSparkConfiguration( boolean all_random){
		SparkConfiguration ret= new SparkConfiguration();
		//遍历每一维度，并生成随机值
		int length0 = Global.CHOSEN_PARAMETERS_GROUP0.length;
		int length1 = Global.CHOSEN_PARAMETERS_GROUP1.length;
		int length2 = Global.CHOSEN_PARAMETERS_GROUP2.length;
		int length3 = Global.CHOSEN_PARAMETERS_GROUP3.length;
		
		String[] randomParameterList=Arrays.copyOf(Global.CHOSEN_PARAMETERS_GROUP0,length0+length1);
		System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP1,0,randomParameterList,length0,length1);
		//String[] chosenParameterList = Arrays.copyOf(Global.CHOSEN_PARAMETERS_GROUP1, length1+length2+length3);//数组扩容并拷贝
	    //System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP2, 0, chosenParameterList, length1, length2);
	    //System.arraycopy(Global.CHOSEN_PARAMETERS_GROUP3, 0, chosenParameterList, length1+length2, length3);
		
		

	    
	    if (!all_random){
	    	 for (int i=0;i<length0;i++){
	    		 randomParameterList[i]="dummy";
	    	 }
	    	 
	    }
	    
		
		for (String key : randomParameterList){
			if (key.equals("dummy")) continue;
			ret.parameterMap.get(key).value=this.scaleMap.get(key).Random();
		}
		return ret;
	}*/

}


//用来表示参数随机变化的范围·上界和下界均可达
class Scale{
	public int upperBound, lowerBound,changeFactor;
	public Scale (int upperBound,int lowerBound, int changeFactor){
		this.upperBound=upperBound;
		this.lowerBound=lowerBound;
		this.changeFactor=changeFactor;
	}
	
	//根据Scale 返回一个随机的值
	public int Random(){
		int ret=this.lowerBound+(int)(Math.floor(Math.random()*getSize()))*this.changeFactor;
		return ret;
	}
	
	/*返回在这个参数空间上，某一维度的自由度大小
	 * */
	public int getSize(){
		 int ret =(int) Math.round((double)((this.upperBound-this.lowerBound)/this.changeFactor+1));
		 return ret;
	}
	
	
}

